using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Xml;
using EnvDTE;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;

namespace DebugCompanion.Engine
{
	static class VsUtil
	{
		public static T GetService<T>()
		{
			T res = (T)(Package.GetGlobalService(typeof(T)));
			if (res == null)
				throw new Exception(string.Format("Can't obtain service {0}", typeof(T).Name));
			return res;
		}

		public static T GetService<T>(Type type)
		{
			T res = (T)(Package.GetGlobalService(type));
			if (res == null)
				throw new Exception(string.Format("Can't obtain service {0}", typeof(T).Name));
			return res;
		}

		public static List<ProjectInfo> GetProjectsList()
		{
			IVsSolution s = GetService<IVsSolution>();
			IEnumHierarchies projectsEnum;
			Guid guid = new Guid();
			ErrorHandler.ThrowOnFailure(
				s.GetProjectEnum((uint)__VSENUMPROJFLAGS.EPF_LOADEDINSOLUTION, ref guid, out projectsEnum));

			List<ProjectInfo> res = new List<ProjectInfo>();
			while (true)
			{
				IVsHierarchy[] tmp = new IVsHierarchy[1];
				uint cnt;
				ErrorHandler.ThrowOnFailure(projectsEnum.Next(1, tmp, out cnt));
				if (cnt == 0)
					break;
				res.Add(new ProjectInfo(tmp[0]));
			}

			return res;
		}

		// the following methods are taken from 
		// http://www.clariusconsulting.net/blogs/kzu/archive/2006/01/06/DteToVSIP.aspx
		public static IVsHierarchy GetCurrentHierarchy(IServiceProvider provider)
		{
			DTE vs = (DTE)provider.GetService(typeof(DTE));
			if (vs == null) throw new InvalidOperationException("DTE not found.");

			return ToHierarchy(vs.SelectedItems.Item(1).ProjectItem.ContainingProject);
		}
		public static IVsHierarchy ToHierarchy(EnvDTE.Project project)
		{
			if (project == null) throw new ArgumentNullException("project");

			string projectGuid = null;

			// DTE does not expose the project GUID that exists at in the msbuild project file.
			// Cannot use MSBuild object model because it uses a static instance of the Engine, 
			// and using the Project will cause it to be unloaded from the engine when the 
			// GC collects the variable that we declare.
			using (XmlReader projectReader = XmlReader.Create(project.FileName))
			{
				projectReader.MoveToContent();
				object nodeName = projectReader.NameTable.Add("ProjectGuid");
				while (projectReader.Read())
				{
					if (Object.Equals(projectReader.LocalName, nodeName))
					{
						projectGuid = projectReader.ReadContentAsString();
						break;
					}
				}
			}

			Debug.Assert(!String.IsNullOrEmpty(projectGuid));

			IServiceProvider serviceProvider = new ServiceProvider(project.DTE as
				Microsoft.VisualStudio.OLE.Interop.IServiceProvider);

			return VsShellUtilities.GetHierarchy(serviceProvider, new Guid(projectGuid));
		}
		public static IVsProject3 ToVsProject(EnvDTE.Project project)
		{
			if (project == null) throw new ArgumentNullException("project");

			IVsProject3 vsProject = ToHierarchy(project) as IVsProject3;

			if (vsProject == null)
			{
				throw new ArgumentException("Project is not a VS project.");
			}

			return vsProject;
		}
		public static EnvDTE.Project ToDteProject(IVsHierarchy hierarchy)
		{
			if (hierarchy == null) throw new ArgumentNullException("hierarchy");

			object prjObject = null;
			if (hierarchy.GetProperty(0xfffffffe, -2027, out prjObject) >= 0)
			{
				return (EnvDTE.Project)prjObject;
			}
			else
			{
				throw new ArgumentException("Hierarchy is not a project.");
			}
		}
		public static EnvDTE.Project ToDteProject(IVsProject project)
		{
			if (project == null) throw new ArgumentNullException("project");

			return ToDteProject(project as IVsHierarchy);
		}
	}

	class ProjectInfo
	{
		public ProjectInfo(IVsHierarchy node)
		{
			_node = node;
			_project = VsUtil.ToDteProject(node);
		}

		private IVsHierarchy _node;

		public IVsHierarchy Node
		{
			[DebuggerStepThrough]
			get { return _node; }
		}

		private Project _project;

		public Project Project
		{
			[DebuggerStepThrough]
			get { return _project; }
		}

		public string Name
		{
			get
			{
				object res;
				_node.GetProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_Name, out res);
				return (string)res;
			}
		}

		public static readonly Guid WebProjectGuid = new Guid("349c5851-65df-11da-9384-00065b846f21");
		public static readonly Guid WebPseudoProjectGuid = new Guid("e24c65dc-7377-472b-9aba-bc803b73c61a");

		public Guid ProjectType
		{
			get
			{
				IVsAggregatableProject aggProj = _node as IVsAggregatableProject;
				if (aggProj == null)
					return new Guid();
				string guids;
				ErrorHandler.ThrowOnFailure(aggProj.GetAggregateProjectTypeGuids(out guids));
				string[] vals = guids.Split(';');
				return new Guid(vals[0]);
			}
		}

		public bool IsExecutable
		{
			get
			{
				if (_project.Properties == null)
					return false;
				Property property = _project.Properties.Item("OutputType");
				return (int)property.Value == 1 || (int)property.Value == 0; // Exe and WinExe
			}
		}

		public override string ToString()
		{
			return Name;
		}

		public void Save()
		{
			Project.Save(Project.FullName);
		}
	}
}
